/*
 * Copyright (c) Huawei Device Co., Ltd. 2023. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "service_manager.h"
#include <string>
#include "loghelper.h"
#include "se_common_exception.h"
#include "service_impl.h"
#include "secure_element_agent.h"
#include "system_ability_definition.h"
#include "se_hisysevent.h"
#include "se_param_manager.h"
#include "se_hianalytics.h"

#ifdef DYNAMIC_LOAD_SE_HOST
#include <idevmgr_hdi.h>

#define SECURE_ELEMENT_HOST_NAME "secure_element_interface_service"

using OHOS::sptr;
using OHOS::HDI::DeviceManager::V1_0::IDeviceManager;
#endif
namespace OHOS {
namespace SE {
const bool REGISTER_RESULT =
    SystemAbility::MakeAndRegisterAbility(DelayedSingleton<ServiceManager>::GetInstance().get());

ServiceManager::ServiceManager() : SystemAbility(SE_MANAGER_SYS_ABILITY_ID, false)
{}

ServiceManager::~ServiceManager()
{}

void ServiceManager::OnStart()
{
    InfoLog("ServiceManager::OnStart start");
    if (runningState_ == ServiceRunningState::STATE_RUNNING) {
        WarnLog("OnStart, ServiceManager has already started.");
        return;
    }

    if (!Init()) {
        ErrorLog("OnStart, failed to init ServiceManager");
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::OTHERS, ErrorCode::SE_INIT_FAILURE);
        return;
    }

    // initialize signature map for access rule cache
    SeParamManager::GetInstance().InitializeParams();
    SeHiAnalytics::GetInstance().SeEventLog(LOG_ON_START, LOG_DEFAULT_STR);
    InfoLog("ServiceManager::OnStart start service success.");
}

void ServiceManager::OnStop()
{
    WarnLog("ServiceManager::OnStop");
#ifdef DYNAMIC_LOAD_SE_HOST
    if (!UnLoadSecureElementHost()) {
        ErrorLog("ServiceManager::UnLoadSecureElementHost failed.");
    }
#endif
    runningState_ = ServiceRunningState::STATE_NOT_START;
    SeHiAnalytics::GetInstance().SeEventLog(LOG_ON_STOP, LOG_DEFAULT_STR);
}

bool ServiceManager::Init()
{
    InfoLog("ServiceManager::Init start.");
    if (!registerToService_) {
        // add all secure elements, mapping the name and the agent to access the hdf.
#ifdef DYNAMIC_LOAD_SE_HOST
        if (!LoadSecureElementHost()) {
            ErrorLog("ServiceManager::LoadSecureElementHost failed.");
        }
#endif
        AddSecureElementAgent();
        if (serviceImpl_ == nullptr) {
            serviceImpl_ = new ServiceImpl(shared_from_this(), secureElementAgents_);
        }
        // AddSecureElementAgent must be executed before Publish. Otherwise, readers may fail to be obtained.
        if (!Publish(serviceImpl_)) {
            ErrorLog("Init(), publish serviceImpl_ failed");
            return false;
        }
        registerToService_ = true;
    }
    runningState_ = ServiceRunningState::STATE_RUNNING;
    InfoLog("ServiceManager::Init success.");
    SeHiAnalytics::GetInstance().SeEventLog(LOG_ON_BIND, eventLog_);
    return true;
}

void ServiceManager::AddSecureElementAgent()
{
    InfoLog("ServiceManager::AddSecureElementAgent start.");
    try {
#ifdef HAS_ESE
        std::shared_ptr<SecureElementAgent> eseAgent = std::make_shared<SecureElementAgent>(SE_NAME_ESE.c_str());
        if (eseAgent->Initialize()) {
            secureElementAgents_.push_back(std::make_pair(SE_NAME_ESE.c_str(), eseAgent));
            eventLog_ = "Has ESE";
            InfoLog("AddSecureElementAgent success for %{public}s", SE_NAME_ESE.c_str());
        } else {
            ErrorLog("AddSecureElementAgent failed for %{public}s", SE_NAME_ESE.c_str());
            SeHiAnalytics::GetInstance().SeEventLog(LOG_ADD_TERMINAL_FAIL, SE_NAME_ESE.c_str());
        }
#endif
#ifdef HAS_SIM
        std::shared_ptr<SecureElementAgent> simAgent = std::make_shared<SecureElementAgent>(SE_NAME_SIM.c_str());
        if (simAgent->Initialize()) {
            secureElementAgents_.push_back(std::make_pair(SE_NAME_SIM.c_str(), simAgent));
            eventLog_.append(" and SIM");
            InfoLog("AddSecureElementAgent success for %{public}s", SE_NAME_SIM.c_str());
        } else {
            ErrorLog("AddSecureElementAgent failed for %{public}s", SE_NAME_SIM.c_str());
            SeHiAnalytics::GetInstance().SeEventLog(LOG_ADD_TERMINAL_FAIL, SE_NAME_SIM.c_str());
        }
        std::shared_ptr<SecureElementAgent> sim2Agent = std::make_shared<SecureElementAgent>(SE_NAME_SIM2.c_str());
        if (sim2Agent->Initialize()) {
            secureElementAgents_.push_back(std::make_pair(SE_NAME_SIM2.c_str(), sim2Agent));
            eventLog_.append(" and SIM2");
            InfoLog("AddSecureElementAgent success for %{public}s", SE_NAME_SIM2.c_str());
        } else {
            ErrorLog("AddSecureElementAgent failed for %{public}s", SE_NAME_SIM2.c_str());
            SeHiAnalytics::GetInstance().SeEventLog(LOG_ADD_TERMINAL_FAIL, SE_NAME_SIM2.c_str());
        }
#endif
    } catch (const std::exception &e) {
        ErrorLog("AddSecureElementAgent, exception failed.");
        SeHiAnalytics::GetInstance().SeEventLog(LOG_GET_SERVICE_FAIL, LOG_DEFAULT_STR);
    }
    InfoLog("ServiceManager::AddSecureElementAgent done, add %{public}lu agent.", secureElementAgents_.size());
}
#ifdef DYNAMIC_LOAD_SE_HOST
bool ServiceManager::LoadSecureElementHost()
{
    InfoLog("ServiceManager::LoadSecureElementHost start.");
    if (!secureElementHostState_) {
        sptr<IDeviceManager> devmgr = IDeviceManager::Get();
        if (devmgr != nullptr) {
            if (devmgr->LoadDevice(SECURE_ELEMENT_HOST_NAME) != 0) {
                ErrorLog("ServiceManager::LoadSecureElementHost LoadDevice failed.");
                return false;
            }
            secureElementHostState_ = true;
        } else {
            ErrorLog("ServiceManager::LoadSecureElementHost get devmgr is nullptr.");
            return false;
        }
    }

    InfoLog("SecureElementHostState is running state:%{public}d.", secureElementHostState_);
    return true;
}

bool ServiceManager::UnLoadSecureElementHost()
{
    InfoLog("ServiceManager::UnLoadSecureElementHost start.");
    if (secureElementHostState_) {
        sptr<IDeviceManager> devmgr = IDeviceManager::Get();
        if (devmgr != nullptr) {
            if (devmgr->UnloadDevice(SECURE_ELEMENT_HOST_NAME) != 0) {
                ErrorLog("ServiceManager::UnLoadSecureElementHost LoadDevice failed.");
                return false;
            }
            secureElementHostState_ = false;
        } else {
            ErrorLog("ServiceManager::UnLoadSecureElementHost get devmgr is nullptr.");
            return false;
        }
    }

    InfoLog("SecureElementHostState is running state:%{public}d.", secureElementHostState_);
    return true;
}
#endif
}  // namespace SE
}  // namespace OHOS